package com.evil.application.util;

import com.evil.application.mapper.ApplicationRobotMapper;
import com.evil.application.pojo.dto.order.AddOrderReqDTO;
import com.evil.application.pojo.dto.order.AppOrdersControlDataDTO;
import com.evil.application.pojo.dto.robot.AppRobotActionReqDTO;
import com.evil.application.pojo.dto.robot.ApplicationRobotRespDTO;
import com.evil.application.pojo.dto.robot.setting.RobotTriggerSettingDTO;
import com.evil.application.pojo.entity.ApplicationOrder;
import com.evil.application.pojo.entity.ApplicationRobot;
import com.evil.application.service.ApplicationRobotService;
import com.evil.application.service.RobotActionService;
import com.evil.common.application.dto.mq.RobotActionMQReqDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.robot.RobotSourceDTO;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.order.OrderProcessStatusEnum;
import com.evil.common.application.enums.robot.RobotFirstTriggerTypeEnum;
import com.evil.common.application.enums.robot.RobotSourceTypeEnum;
import com.evil.common.application.util.ApplicationRedisKeyUtil;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.RedisUtil;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.core.util.spring.SpringUtils;
import com.evil.common.log.annotation.ExecuteTime;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 机器人工具 （因为性能问题 由AOP 处理调整成工具类处理）
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Component
@AllArgsConstructor
public class RobotUtil {

    private final RedisUtil redisUtil;

    private final MatchingUtil matchingUtil;

    private final ApplicationRobotMapper applicationRobotMapper;

    private final ApplicationRobotService applicationRobotService;

    /**
     * 通用编辑型
     *
     * @param order   order
     * @param data    data
     * @param oldData oldData
     * @return 程序是否往下执行
     */
    public boolean before(String applicationKey, ApplicationOrder order, List<ControlDataDTO> data, List<ControlDataDTO> oldData) {
        AppRobotActionReqDTO appRobotActionReqDTO = new AppRobotActionReqDTO();
        appRobotActionReqDTO.setTriggerTypeEnum(RobotFirstTriggerTypeEnum.ON_MODIFY);
        // 当前层级
        appRobotActionReqDTO.setApplicationKey(applicationKey);
        return this.prepareRobot(appRobotActionReqDTO, order, data, oldData, false);
    }

    /**
     * 通用批量编辑型
     *
     * @param applicationKey applicationKey
     * @param orders         orders
     * @param dataMap        dataMap
     * @param oldDataMap     oldDataMap
     * @return 程序是否往下执行
     */
    public boolean before(String applicationKey, Collection<ApplicationOrder> orders, Map<Long, List<ControlDataDTO>> dataMap, Map<Long, List<ControlDataDTO>> oldDataMap) {
        AppRobotActionReqDTO appRobotActionReqDTO = new AppRobotActionReqDTO();
        appRobotActionReqDTO.setTriggerTypeEnum(RobotFirstTriggerTypeEnum.ON_MODIFY);
        // 当前层级
        appRobotActionReqDTO.setApplicationKey(applicationKey);

        List<ApplicationOrder> noChangeOrders = new ArrayList<>();
        orders.forEach(order -> {
            Long orderId = order.getOrderId();
            if (!dataMap.containsKey(orderId) || !oldDataMap.containsKey(orderId)) {
                throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
            }
            List<ControlDataDTO> data = dataMap.get(orderId);
            List<ControlDataDTO> oldData = oldDataMap.get(orderId);
            if (!this.prepareRobot(appRobotActionReqDTO, order, data, oldData, true)) {
                noChangeOrders.add(order);
            }
        });
        if (noChangeOrders.size() == orders.size()) {
            return false;
        }
        // 移除没有修改的订单
        noChangeOrders.forEach(orders::remove);
        return true;
    }

    /**
     * 批量编辑专用
     *
     * @param appOrdersControlDataDTO appOrdersControlDataDTO
     * @param orderMap                orderMap
     * @param oldDataMap              oldDataMap
     * @return 程序是否往下执行
     */
    public boolean before(AppOrdersControlDataDTO appOrdersControlDataDTO, Map<Long, ApplicationOrder> orderMap, Map<Long, List<ControlDataDTO>> oldDataMap) {
        AppRobotActionReqDTO appRobotActionReqDTO = new AppRobotActionReqDTO();
        appRobotActionReqDTO.setTriggerTypeEnum(RobotFirstTriggerTypeEnum.ON_MODIFY);
        // 当前层级
        appRobotActionReqDTO.getLogAppKeys().addAll(appOrdersControlDataDTO.getLogAppKeys());
        appRobotActionReqDTO.setApplicationKey(appOrdersControlDataDTO.getApplicationKey());

        List<Long> noChangeOrderIds = new ArrayList<>();
        appOrdersControlDataDTO.getOrdersDataListMap().forEach((orderId, data) -> {
            if (!orderMap.containsKey(orderId) || !oldDataMap.containsKey(orderId)) {
                throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
            }
            if (!this.prepareRobot(appRobotActionReqDTO, orderMap.get(orderId), data, oldDataMap.get(orderId), true)) {
                noChangeOrderIds.add(orderId);
            }
        });
        if (noChangeOrderIds.size() == appOrdersControlDataDTO.getOrdersDataListMap().size()) {
            return false;
        }
        // 移除没有修改的订单
        noChangeOrderIds.forEach(orderId -> appOrdersControlDataDTO.getOrdersDataListMap().remove(orderId));
        return true;
    }

    /**
     * 新增订单专用
     *
     * @param addOrderReqDTO addOrderReqDTO
     * @param order          order
     * @param data           data
     */
    public void after(AddOrderReqDTO addOrderReqDTO, ApplicationOrder order, List<ControlDataDTO> data) {
        AppRobotActionReqDTO appRobotActionReqDTO = new AppRobotActionReqDTO();
        appRobotActionReqDTO.setTriggerTypeEnum(RobotFirstTriggerTypeEnum.ON_ADD);
        // 当前层级
        appRobotActionReqDTO.getLogAppKeys().addAll(addOrderReqDTO.getLogAppKeys());
        appRobotActionReqDTO.setApplicationKey(addOrderReqDTO.getApplicationKey());
        // 草稿箱不触发机器人
        if (OrderProcessStatusEnum.DRAFT_BOX.getId() != addOrderReqDTO.getOrderProcessStatus()) {
            this.prepareRobot(appRobotActionReqDTO, order, data, data, false);
        }
    }

    /**
     * 执行机器人（同步/异步）
     */
    public void robotMq() {
        while (true) {
            RobotActionMQReqDTO mq = (RobotActionMQReqDTO) redisUtil.rpop(ApplicationRedisKeyUtil.robotMqKey());
            if (null == mq) {
                break;
            }
            SpringUtils.getBean(RobotActionService.class).robotActionBeforeLog(mq);
        }
    }

    /**
     * 执行机器人，返回是否还需要往下执行
     *
     * @param appRobotActionReqDTO appRobotActionReqDTO
     * @param order                order
     * @param data                 data
     * @param checkChange          checkChange
     * @return boolean
     */
    private boolean prepareRobot(AppRobotActionReqDTO appRobotActionReqDTO, ApplicationOrder order, List<ControlDataDTO> data, List<ControlDataDTO> oldData, boolean checkChange) {
        if (checkChange) {
            if (!OrderDataUtil.hasChange(data, oldData, null)) {
                return false;
            }
        }
        appRobotActionReqDTO.setOrderId(order.getOrderId());
        appRobotActionReqDTO.setData(data);
        appRobotActionReqDTO.setOldData(oldData);
        appRobotActionReqDTO.setSource(new RobotSourceDTO(RobotSourceTypeEnum.APPLICATION_ROBOT, order.getApplicationId(), ApplicationVersionEnum.RELEASE_VERSION.getId()));

        // 当前层级 如果已经出现过了，不允许触发下一层机器人
        long count = appRobotActionReqDTO.getLogAppKeys().stream().filter(e -> e.equals(appRobotActionReqDTO.getApplicationKey())).count();
        if (count <= 2) {
            this.prepareRobot(appRobotActionReqDTO);
        }
        return true;
    }

    /**
     * 准备机器人
     *
     * @param appRobotActionReqDTO appRobotActionReqDTO
     */
    @ExecuteTime(name = "【prepareRobot】")
    public void prepareRobot(AppRobotActionReqDTO appRobotActionReqDTO) {
        RobotFirstTriggerTypeEnum robotFirstTriggerTypeEnum = appRobotActionReqDTO.getTriggerTypeEnum();
        RobotSourceDTO source = appRobotActionReqDTO.getSource();
        Long orderId = appRobotActionReqDTO.getOrderId();
        List<ControlDataDTO> data = appRobotActionReqDTO.getData();
        List<ControlDataDTO> oldData = appRobotActionReqDTO.getOldData();

        List<ApplicationRobotRespDTO> robotInfoList = this.robotInfoList(source, robotFirstTriggerTypeEnum);
        if (robotInfoList.isEmpty()) {
            return;
        }

        // 当前订单数据map （这里克隆一份以免影响其他位置内存指向）
        List<ControlDataDTO> copyData = OrderDataUtil.copyAndSupply(oldData, data);
        Map<Long, ControlDataDTO> copyDataMap = StreamUtil.toOrderlyMapK(copyData, ControlDataDTO::getControlId);
        for (ApplicationRobotRespDTO robot : robotInfoList) {
            // 更新数据时
            if (RobotFirstTriggerTypeEnum.ON_MODIFY.getId() == robotFirstTriggerTypeEnum.getId()) {
                RobotTriggerSettingDTO triggerSetting = robot.getTriggerSetting();
                // 哪些控件需要比对
                List<Long> controlIds = triggerSetting.isTriggerControlStatus() ? triggerSetting.getTriggerControlIds() : null;
                if (!OrderDataUtil.hasChange(copyData, oldData, controlIds)) {
                    // 没有变更
                    continue;
                }
            }
            // 筛选条件
            if (!robot.getRangeJudges().isEmpty() && !OrderDataUtil.handleCondition(robot.getRangeJudges(), copyDataMap, matchingUtil::dynamic)) {
                // 不满足
                continue;
            }

            // 线程记录机器人队列（主方法结束后，即调用RobotUtil.robotMq，根据消息sync参数决定，发mq队列还是同步执行）
            RobotActionMQReqDTO robotActionReqDTO = new RobotActionMQReqDTO();
            robotActionReqDTO.setTargetAppId(robot.getTargetApplicationId());
            robotActionReqDTO.setJudgeOrs(robot.getJudgeOrs());
            robotActionReqDTO.setRobotRules(robot.getRobotRules());
            robotActionReqDTO.setOrderId(orderId);
            robotActionReqDTO.setTriggerType(robot.getTriggerType());
            robotActionReqDTO.setRecalculation(robot.getRecalculation());
            robotActionReqDTO.setRecalculationControlIds(robot.getRecalculationControlIds());
            robotActionReqDTO.setSaveNow(true);
            robotActionReqDTO.setApplicationKey(appRobotActionReqDTO.getApplicationKey());
            robotActionReqDTO.setLogAppKeys(appRobotActionReqDTO.getLogAppKeys());
            robotActionReqDTO.setSync(robot.getSync());
            robotActionReqDTO.setTriggerId(robot.getRobotId());
            robotActionReqDTO.setTriggerName(robot.getRobotName());
            robotActionReqDTO.setRobotSourceTypeEnum(RobotSourceTypeEnum.APPLICATION_ROBOT);
            redisUtil.lpush(ApplicationRedisKeyUtil.robotMqKey(), robotActionReqDTO);
        }
    }

    private List<ApplicationRobotRespDTO> robotInfoList(RobotSourceDTO source, RobotFirstTriggerTypeEnum robotFirstTriggerTypeEnum) {
        // 获取打开状态的机器人
        source.setFirstTriggerType(robotFirstTriggerTypeEnum.getId());
        source.setRobotStatus(SwitchEnum.YES.getId());
        List<ApplicationRobot> applicationRobots = applicationRobotMapper.findBySource(source);
        if (applicationRobots.isEmpty()) {
            return new ArrayList<>();
        }
        return applicationRobotService.perfectRobotInfo(applicationRobots);
    }
}
